package com.apes.fico.finance.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.apes.fico.engine.FicoUtil;
import com.apes.fico.engine.model.VoucherOwnerConfig;
import com.apes.fico.engine.repository.BusinessDocumentRepository;
import com.apes.fico.engine.repository.VoucherOwnerConfigRepository;
import com.apes.fico.finance.model.gl.GLVoucher;
import com.apes.fico.finance.model.gl.GLVoucherAuxiliary;
import com.apes.fico.finance.model.gl.GLVoucherItem;
import com.apes.fico.finance.model.gl.GLVoucherPool;
import com.apes.fico.finance.respository.GLVoucherAuxiliaryRepository;
import com.apes.fico.finance.respository.GLVoucherPoolRepository;
import com.apes.fico.finance.respository.GLVoucherRepository;
import com.apes.fico.masterdata.gl.model.AccountingOperatingChart;
import com.apes.fico.masterdata.gl.model.AccountingOperatingChartAuxiliary;
import com.apes.fico.masterdata.gl.model.AccountingPeriod;
import com.apes.fico.masterdata.gl.model.AccountingPeriodItem;
import com.apes.fico.masterdata.gl.respository.AccountingOperatingChartRepository;
import com.apes.fico.masterdata.gl.respository.AccountingPeriodItemRepository;
import com.apes.fico.masterdata.gl.respository.AccountingPeriodRepository;
import com.apes.framework.api.DomainService;
import com.apes.framework.api.SpringManager;
import com.apes.framework.jpa.generator.service.SequenceService;
import com.apes.framework.jpa.repository.CustomRepository;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.framework.plugin.mq.MQUtil;
import com.apes.framework.plugin.mq.annotation.Consumer;
import com.apes.framework.plugin.outbox.OutboxEvent;
import com.apes.framework.plugin.outbox.OutboxEventService;
import com.apes.framework.plugin.outbox.OutboxPayloadWrapper;
import com.apes.framework.plugin.outbox.OutboxSubscriberService;
import com.apes.framework.plugin.outbox.annotation.OutboxSubscriber;
import com.apes.framework.util.*;
import com.apes.oap.RopException;
import com.apes.scm.masterdata.party.model.PartyRole;
import com.apes.scm.masterdata.party.repository.PartyRoleRepository;
import com.apes.scm.masterdata.party.service.PartyRoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * @ClassName GLVoucherService
 * @Description TODO
 * @Author pengyb
 * @Date 2019/1/5 11:00
 * @Version 1.0
 **/
@Service("glVoucherService")
public class GLVoucherService extends DomainService {
    @Autowired
    private GLVoucherRepository glVoucherRepository;
    @Autowired
    private GLVoucherPoolRepository glVoucherPoolRepository;
    @Autowired
    private AccountingPeriodRepository accountingPeriodRepository;
    @Autowired
    private AccountingPeriodItemRepository accountingPeriodItemRepository;

    @Autowired
    private VoucherOwnerConfigRepository voucherOwnerConfigRepository;

    public static final String APPROVE_GLVOUCHER = "finance.GLVoucher.approve";


    /**
     * 保存
     *
     * @param request
     * @return
     */
    public GLVoucher save(SimpleRequest request) throws Exception {
        GLVoucher glVoucher = request.getO(GLVoucher.class);
        String id = SpringManager.getBean(SequenceService.class).getId(GLVoucher.CONST_TABLE_NAME);
        glVoucher.setId(id);
        glVoucher.setCreator(this.getPersonId(request));
        if(glVoucher.isHandworkFlag()) checkItem(glVoucher);
        checkCarryOver(glVoucher, "已结转损溢不允许增加凭证,请取消结账");

        glVoucher.checkDebitAndCredit();
        glVoucher.setCreateDate(new Date());
        glVoucher.checkVoucherNo();

        processGLVoucherItem(glVoucher);
        checkAux(glVoucher);
        glVoucher = glVoucherRepository.saveAndFlush(glVoucher);

        writeChart(glVoucher, 1, 0, "创建凭证");

        // 当生成损益结转凭证时，直接审核
        // 是否凭证模板生成
        if (glVoucher.getVoucherTemplate() != null && "998".equals(glVoucher.getVoucherTemplate().getVoucherTemplateGroup().getId())) {
            //是否损益结转(998固定损益结转)
            this.invoke(APPROVE_GLVOUCHER, MapUtil.mapped(glVoucher));
        }
        return glVoucher;
    }

    private void checkItem(GLVoucher glVoucher) {
        //只判断成本类和损益收入类和损益费用类三个科目组
        glVoucher.getVoucherItemList().stream().filter(glVoucherItem -> LogicalUtil.in(glVoucherItem.getAccountingOperatingChart().getAccGroup().getId(),"4","5","6")).forEach(glVoucherItem -> {
            if("0".equals(glVoucherItem.getAccountingOperatingChart().getBalanorient()) && glVoucherItem.getCredit() != 0){
                throw new RuntimeException("科目："+glVoucherItem.getAccountingOperatingChart().getName()+" 为借方科目，不允许填写贷方金额");
            } else if ("1".equals(glVoucherItem.getAccountingOperatingChart().getBalanorient()) && glVoucherItem.getDebit() != 0){
                throw new RuntimeException("科目："+glVoucherItem.getAccountingOperatingChart().getName()+"  为贷方科目，不允许填写借方金额");
            }
        });
    }

    private void checkAux(GLVoucher glVoucher){
        glVoucher.getVoucherItemList().forEach(glVoucherItem -> {
            if(!glVoucherItem.getAccountingOperatingChart().isAuxFlag() && glVoucherItem.getAux() != null)
                throw new RuntimeException("科目："+glVoucherItem.getAccountingOperatingChart().getName()+"不应有辅助核算");
        });
    }

    /**
     * 修改
     *
     * @param request
     * @return
     */
    public GLVoucher update(SimpleRequest request) {
        checkData(request);
        GLVoucher glVoucher = request.getO(GLVoucher.class);
        glVoucher.checkDebitAndCredit();

        GLVoucher glVouchers = glVoucherRepository.findOne((String) (request.get("id")));
        writeChart(glVouchers, -1, 0, "修改凭证：还原");

        glVoucher = checkSoliton(glVoucher);
        processGLVoucherItem(glVoucher);
        glVoucher = glVoucherRepository.saveAndFlush(glVoucher);
        if(glVoucher.isHandworkFlag()) checkItem(glVoucher);
        writeChart(glVoucher, 1, 0, "修改凭证");

        return glVoucher;
    }

    public void processGLVoucherItem(GLVoucher glVoucher) {
        String id = glVoucher.getId();
        if (!glVoucher.getVoucherItemList().isEmpty() || glVoucher.getVoucherItemList().size() != 0) {
            List<GLVoucherItem> voucherItemList = glVoucher.getVoucherItemList();
            int size = voucherItemList.size();

            IntStream.range(0, size).forEach(
                    i -> {
                        GLVoucherItem voucherItem = voucherItemList.get(i);
                        String itemId = voucherItem.getId();
                        if (itemId == null) {
                            List<GLVoucherItem> glVoucherItems = glVoucher.getVoucherItemList().stream().filter(glVoucherItem -> glVoucherItem.getId() != null).collect(Collectors.toList());
                            if(!glVoucherItems.isEmpty()){
                                GLVoucherItem glVoucherItem = glVoucherItems.get(glVoucherItems.size() - 1);
                                String theId = glVoucherItem.getId().substring(glVoucherItem.getId().indexOf(".") + 1);
                                int count = Integer.parseInt(theId) + 1;
                                voucherItem.setId(new StringBuffer(id).append(".").append(count).toString());
                            } else {
                                voucherItem.setId(new StringBuffer(id).append(".").append(i).toString());
                            }
                        }
                        voucherItem.setOrderNum(i);
                        if (voucherItem.getVoucherAuxiliaryList() != null) {
                            String collectAux = FicoUtil.createCollectKey(voucherItem.getVoucherAuxiliaryList(),
                                    tva -> ((GLVoucherAuxiliary) tva).getAccountingOperatingChartAuxiliary().getId() + "," + ((GLVoucherAuxiliary) tva).getCode());
                            voucherItem.setCollectAux(collectAux);
                            voucherItem.getVoucherAuxiliaryList().stream().filter(bean -> {
                                bean.setId(null);
                                bean.setCollectAux(collectAux);
                                return true;
                            }).collect(Collectors.toList());
                        }
                    });
        }
    }

    /**
     * 删除
     *
     * @param request
     * @return
     */
    public String delete(SimpleRequest request) {
        GLVoucher glVoucher = glVoucherRepository.findOne((String) request.get("id"));
        if (glVoucher == null) {
            throw new RuntimeException("财务凭证" + request.get("id") + "不存在！");
        }
        if (!"sent".equals(glVoucher.getState())) {
            throw new RuntimeException(String.format("凭证已%s，不允许修改", glVoucher.getReversalUID() != null ? "冲红" : "审核"));
        }
        return deleteGLVoucher(glVoucher);
    }

    public String deleteGLVoucher(GLVoucher glVoucher){

        /**凭证号归还凭证池**/
        restoreVoucherNo(glVoucher);

        if (glVoucher.getBusinessType() != null && glVoucher.getBusinessId() != null) {
            BusinessDocumentRepository repository = SpringManager.getBean(BusinessDocumentRepository.class);
            repository.deleteByBusinessTypeAndBusinessIdAndVoucherTemplate(glVoucher.getBusinessType(), glVoucher.getBusinessId(), glVoucher.getVoucherTemplate());
        }

        writeChart(glVoucher, -1, 0, "删除凭证");

        glVoucherRepository.delete(glVoucher);
        return "删除成功";
    }

    /**
     * 凭证号归还凭证池
     **/
    private void restoreVoucherNo(GLVoucher glVoucher) {
        GLVoucherPool glVoucherPool = glVoucherPoolRepository.findByCompanyAndPeriodAndYearAndVoucherNoAndHandworkFlag(glVoucher.getCompany(),
                glVoucher.getPeriod(), glVoucher.getYear(), glVoucher.getVoucherNo(), glVoucher.isHandworkFlag());
        glVoucherPool.setCancel(1);
        glVoucherPool.setHandworkFlag(glVoucher.isHandworkFlag());
        glVoucherPoolRepository.saveAndFlush(glVoucherPool);
    }

    /**
     * 查询
     *
     * @param request
     * @return
     */
    public Page<GLVoucher> findAll(SimpleRequest request) {
        List<Map> filters = request.get("condition");
        Sort sort = new Sort(Sort.Direction.DESC, "voucherItemList.orderNum");
        Page<GLVoucher> glVoucher = glVoucherRepository.findAll(JpaDsl.toCriteria(filters), PageRequest.of(request.get("page"), request.get("size"), sort));
        return glVoucher;
    }

    /**
     * 审核
     */
    public GLVoucher approve(SimpleRequest request) {
        checkData(request);
        GLVoucher glVoucher = request.getO(GLVoucher.class);
        glVoucher.setApproveUID(getPerson(request));
        return approveVoucher(glVoucher, "审核凭证");
    }

    private GLVoucher approveVoucher(GLVoucher glVoucher, String remarks) {
        glVoucher.setState("approve");
        glVoucher.setApproveDate(new Date());
        glVoucher = glVoucherRepository.saveAndFlush(glVoucher);
        writeChart(glVoucher, -1, 1, remarks);
        return glVoucher;
    }

    /**
     * 反审核
     *
     * @param request
     * @return
     */
    public GLVoucher overApprove(SimpleRequest request) {
        checkData(request);
        GLVoucher glVoucher = request.getO(GLVoucher.class);
        if (!glVoucher.getCancelCarryOverPL()) {
            checkCarryOver(glVoucher, "已结转损溢不允许反审核,请取消结转");
        }
        if(!"approve".equals(glVoucher.getState())) throw new RuntimeException("未审核单据不允许反审核");
        glVoucher.setState("sent");
        glVoucher.setApproveUID(null);
        glVoucher.setApproveDate(null);
        glVoucher = glVoucherRepository.saveAndFlush(glVoucher);
        writeChart(glVoucher, 1, -1, "反审核凭证");
        return glVoucher;
    }

    /**
     * 凭证冲红
     *
     * @param request
     * @return
     */
    public GLVoucher reversal(SimpleRequest request) {
        checkData(request);
        //冲红的凭证
        GLVoucher reversalVoucher = request.getO(GLVoucher.class);
        //冲红生成的新凭证
        GLVoucher voucher = new GLVoucher();

        checkReversal(reversalVoucher);

        JSONObject jsonObject = new JSONObject();
        Map map = new HashMap();
        jsonObject.put("postDate", LocalDate.now().toString());
        jsonObject.put("company", reversalVoucher.getCompany().getId());
        SimpleRequest rq = new SimpleRequest();
        rq.setBody(jsonObject.toString());
        try {
            map = checkPostDate(rq);
        } catch (ParseException e) {
            throw new RopException(e);
        }

        voucher.setPostDate(new Date());
        voucher.setPeriod(map.get("period").toString());
        voucher.setYear(Integer.parseInt(map.get("year").toString()));
        voucher.setMonth(Integer.parseInt(map.get("month").toString()));
        voucher.setYearMonth(Integer.parseInt(map.get("yearMonth").toString()));
        voucher.setVoucherTemplate(reversalVoucher.getVoucherTemplate());
        voucher.setDebitTotal(reversalVoucher.getDebitTotal() * -1.0);
        voucher.setCreditTotal(reversalVoucher.getCreditTotal() * -1.0);
        voucher.setCompany(reversalVoucher.getCompany());
        voucher.setRemark(reversalVoucher.getId() + " 冲红生成的财务凭证");
        voucher.setBusinessType(reversalVoucher.getBusinessType());
        voucher.setBusinessId(reversalVoucher.getBusinessId());

        if (reversalVoucher.getYearMonth() == voucher.getYearMonth())
            throw new RuntimeException("无效操作！(当月的帐无需冲红，请自行修改)");

        voucher.setAccountingVoucherType(reversalVoucher.getAccountingVoucherType());
        voucher.getVoucherItemList().clear();
        List<GLVoucherItem> glVoucherItems = new ArrayList<>();

        reversalVoucher.getVoucherItemList().forEach(glVoucherItem -> {
            GLVoucherItem item = new GLVoucherItem();
            item.setAccountingOperatingChart(glVoucherItem.getAccountingOperatingChart());
            item.setCredit(glVoucherItem.getCredit() * -1.0);
            item.setDebit(glVoucherItem.getDebit() * -1.0);
            item.setAux(glVoucherItem.getAux());
            item.setName(glVoucherItem.getName());
            item.setKey(glVoucherItem.getKey());
            item.setSummary(glVoucherItem.getSummary());
            item.setCollectAux(glVoucherItem.getCollectAux());
            List<GLVoucherAuxiliary> glVoucherAuxiliaries = new ArrayList<>();
            glVoucherItem.getVoucherAuxiliaryList().forEach(glVoucherAuxiliary -> {
                GLVoucherAuxiliary auxiliary = new GLVoucherAuxiliary();
                auxiliary.setAccountingOperatingChartAuxiliary(glVoucherAuxiliary.getAccountingOperatingChartAuxiliary());
                auxiliary.setCode(glVoucherAuxiliary.getCode());
                auxiliary.setName(glVoucherAuxiliary.getName());
                auxiliary.setCollectAux(glVoucherAuxiliary.getCollectAux());
                glVoucherAuxiliaries.add(auxiliary);

            });
            item.setVoucherAuxiliaryList(glVoucherAuxiliaries);
            glVoucherItems.add(item);
        });
        voucher.getVoucherItemList().addAll(glVoucherItems);

        Object result = this.invoke("finance.GLVoucher.create", MapUtil.mapped(voucher));
        this.invoke("finance.GLVoucher.approve", MapUtil.mapped(result));

        //调整原单据为冲红状态
        reversalVoucher.setReversalUID(request.getPerson());
        reversalVoucher.setReversalDate(new Date());
        reversalVoucher.setReversalVoucher(voucher.getId());
        reversalVoucher.setState("reversal");

        return glVoucherRepository.saveAndFlush(reversalVoucher);
    }


    /**
     * 写科目余额帐
     * @param glVoucher  财务凭证
     * @param unsign  未记账帐户标记（值：-1,0,1）
     * @param sign 记账帐户标记（值：-1,0,1）
     * @param remarks 备注
     */
    private void writeChart(GLVoucher glVoucher, double unsign, double sign, String remarks) {
        glVoucher.getVoucherItemList().stream().forEach(glVoucherItem -> {
            boolean auxed = glVoucherItem.getVoucherAuxiliaryList().size() != 0;
            boolean isAuxFlag = glVoucherItem.getAccountingOperatingChart()
                    .getAccOperatingChartAuxiliaryList()
                    .stream()
                    .filter(e -> !e.getAccAuxiliaryProject().isCashFlow()).count() > 0;
            if (!auxed && isAuxFlag && LogicalUtil.in(remarks,"审核凭证","批量审核凭证")) {
                throw new RuntimeException(String.format("科目[%s]有辅助核算项目，核算项目不允许为空！", glVoucherItem.getAccountingOperatingChart().getId()));
            }
            Map row = MapUtil.mapper(
                    "companyId", glVoucher.getCompany().getId(),
                    "aocId", glVoucherItem.getAccountingOperatingChart().getId(),
                    "year", glVoucher.getYear(),
                    "period", glVoucher.getYearMonth(),
                    "unDebit", unsign * glVoucherItem.getDebit(),
                    "unCredit", unsign * glVoucherItem.getCredit(),
                    "debit", sign * glVoucherItem.getDebit(),
                    "credit", sign * glVoucherItem.getCredit(),
                    "voucherId", glVoucher.getId(),
                    "voucherItemId", glVoucherItem.getId(),
                    "remarks", remarks,
                    "auxed", auxed
            );
            if (auxed) { /**有辅助核算**/
                row.put("key", glVoucherItem.getCollectAux());
                if(glVoucherItem.getName() == null || glVoucherItem.getName().isEmpty()) throw new RuntimeException("第："+glVoucherItem.getOrderNum() + 1+"行没有辅助核算,请检查"+glVoucherItem.getId());
                row.put("name", glVoucherItem.getName());
                row.put("chartAuxItems", glVoucherItem.getVoucherAuxiliaryList().stream().map(caItem -> MapUtil.mapper(
                        "aocaId", caItem.getAccountingOperatingChartAuxiliary().getId(),
                        "code", caItem.getCode(),
                        "name", caItem.getName()
                )).collect(Collectors.toList()));
            }

            OutboxPayloadWrapper payloadWrapper = new OutboxPayloadWrapper();
            payloadWrapper.setSubscriber("local:finance.chartAux.write");
            payloadWrapper.setAggregateId(glVoucherItem.getId());
            payloadWrapper.setPayload(JSON.toJSONString(row));
            SpringManager.getBean(OutboxEventService.class).sendSaveEvent(payloadWrapper);
        });
    }

    @OutboxSubscriber(id = "local:finance.chartAux.write", name = "写入每期发生额")
    public void writeFinanceChart(OutboxPayloadWrapper outboxPayloadWrapper) {
        JSONObject payload = JSON.parseObject(outboxPayloadWrapper.getPayload());
        ChartService chartService = SpringManager.getBean(ChartService.class);
        PartyRole company = SpringManager.getBean(PartyRoleRepository.class).findOne(payload.getString("companyId"));
        AccountingOperatingChart accountingOperatingChart = SpringManager.getBean(AccountingOperatingChartRepository.class).findOne(payload.getString("aocId"));
        if (payload.getBoolean("auxed")) { /**有辅助核算**/
            chartService.saveChartAux(company, accountingOperatingChart, payload.getIntValue("year"), payload.getIntValue("period"),
                    payload.getString("key"), payload.getString("name"), payload.getJSONArray("chartAuxItems").toJavaList(Map.class),
                    payload.getDouble("unDebit"), payload.getDouble("unCredit"), payload.getDouble("debit"), payload.getDouble("credit"),
                    payload.getString("voucherId"), payload.getString("voucherItemId"), payload.getString("remarks"));
        } else {
            chartService.saveChartAmount(company, accountingOperatingChart, payload.getIntValue("year"), payload.getIntValue("period"),
                    payload.getDouble("unDebit"), payload.getDouble("unCredit"), payload.getDouble("debit"), payload.getDouble("credit"),
                    payload.getString("voucherId"), payload.getString("voucherItemId"), payload.getString("remarks"));
        }
    }

    //临时凭证生成凭证
    public void manualVoucher(SimpleRequest request) {
        GLVoucher glVoucher = request.getO(GLVoucher.class);
        glVoucher.getPostDate();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(glVoucher.getPostDate());
        AccountingPeriod accountingPeriod = accountingPeriodRepository.findByCompany_IdAndYear(glVoucher.getCompany().getId(), calendar.get(Calendar.YEAR));
        if (accountingPeriod == null) {
            throw new RuntimeException("公司:" + glVoucher.getCompany().getId() + "年份" + calendar.get(Calendar.YEAR) + "未创建会计期间！");
        }
        VoucherOwnerConfig voucherOwnerConfig = voucherOwnerConfigRepository.findByCompany_IdAndVoucherTemplate(glVoucher.getCompany().getId(), glVoucher.getVoucherTemplate().getId());
        if (voucherOwnerConfig != null) {
            glVoucher.setCreator(voucherOwnerConfig.getPerson().getId());
        }
        AccountingPeriodItem accountingPeriodItem = accountingPeriod.getAccountingPeriodItems().stream().filter(accountingPeriodItems -> accountingPeriodItems.getMonth() == calendar.get(Calendar.MONTH) + 1).collect(Collectors.toList()).get(0);
        glVoucher.setPeriod(accountingPeriodItem.getPeriod_name());
        glVoucher.setYear(accountingPeriod.getYear());
        glVoucher.setYearMonth(accountingPeriodItem.getYearMonth());
        glVoucher.setMonth(accountingPeriodItem.getMonth());
        glVoucher.setState("sent");
        glVoucher.setHandworkFlag(false);
//        glVoucher.getVoucherItemList().forEach(glVoucherItem -> {
//            if (glVoucherItem.getAccountingOperatingChart().getAccOperatingChartAuxiliaryList().size() != 0 && !glVoucherItem.getAccountingOperatingChart().getAccOperatingChartAuxiliaryList().isEmpty()) {
//                glVoucherItem.getAccountingOperatingChart().getAccOperatingChartAuxiliaryList().addAll(accountingOperatingChartRepository.findOne(glVoucherItem.getAccountingOperatingChart().getId()).getAccOperatingChartAuxiliaryList());
//            } else {
//                glVoucherItem.getAccountingOperatingChart().getAccOperatingChartAuxiliaryList().addAll(new ArrayList<>());
//            }
//        });

        this.invoke("finance.GLVoucher.create", MapUtil.mapped(glVoucher));

    }

    /**
     * 级联更新去孤子
     *
     * @param glVoucher
     * @return
     */
    private GLVoucher checkSoliton(GLVoucher glVoucher) {
        glVoucher.getVoucherItemList().forEach(glVoucherItem -> {
            List<GLVoucherAuxiliary> glVoucherAuxiliaries = new ArrayList<>();
            if (!glVoucherItem.getVoucherAuxiliaryList().isEmpty() && glVoucherItem.getVoucherAuxiliaryList().size() != 0) {
                glVoucherItem.getVoucherAuxiliaryList().forEach(aux -> {
                    GLVoucherAuxiliary glVoucherAuxiliary = new GLVoucherAuxiliary();
                    glVoucherAuxiliary.setId(aux.getId());
                    glVoucherAuxiliary.setVoucherItem(aux.getVoucherItem());
                    glVoucherAuxiliary.setName(aux.getName());
                    glVoucherAuxiliary.setCode(aux.getCode());
                    glVoucherAuxiliary.setAccountingOperatingChartAuxiliary(aux.getAccountingOperatingChartAuxiliary());
                    glVoucherAuxiliaries.add(glVoucherAuxiliary);
                });

            }
            glVoucherItem.setVoucherAuxiliaryList(glVoucherAuxiliaries);
        });
        return glVoucher;
    }


    /**
     * 检查结转
     *
     * @param glVoucher
     */
    public void checkCarryOver(GLVoucher glVoucher, String msg) {
        GLVoucher checkglVouchers = glVoucherRepository.findByPeriodAndCompany_IdAndVoucherTemplate_VoucherTemplateGroup_Id(glVoucher.getPeriod(), glVoucher.getCompany().getId(), "998");
        if (checkglVouchers != null) {
            throw new RuntimeException(msg);
        }
    }


    /**
     * 检查
     *
     * @param request
     */
    public void checkData(SimpleRequest request) {
        GLVoucher glVoucher = glVoucherRepository.findOne((String) request.get("id"));
        if (glVoucher == null) throw new RuntimeException("财务凭证" + glVoucher.getId() + "不存在！");
        if (glVoucher.getVersion() != (int) request.get("version")) throw new RuntimeException("状态已改变,请重新查询！");
        if (glVoucher.getReversalUID() != null && glVoucher.getApproveUID() != null)
            throw new RuntimeException("凭证已冲红，不允许修改");
    }

    /**
     * 根据记账日期给出会计期间
     *
     * @param request
     * @return
     * @throws ParseException
     */
    public Map checkPostDate(SimpleRequest request) throws ParseException {
        String postDate = request.get("postDate");
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date date = sdf.parse(postDate);
        if (date.after(new Date())) throw new RuntimeException("记账日期不得在当前日期之后");
        AccountingPeriodItem accountingPeriodItem = accountingPeriodItemRepository.findByPostDateAndCompany(date, request.get("company"));
        if (accountingPeriodItem == null) throw new RuntimeException("会计期间档案未定义此记帐日期！请检查");
        List<GLVoucher> glVouchers = glVoucherRepository.findAllByPeriodAndCompany_Id(accountingPeriodItem.getPeriod_name(), request.get("company"));
        if (!glVouchers.isEmpty()) throw new RuntimeException("会计期间：" + accountingPeriodItem.getPeriod_name() + "已结转！请检查操作");
        Map map = new HashMap();
        map.put("period", accountingPeriodItem.getPeriod_name());
        map.put("year", accountingPeriodItem.getAccountingPeriod().getYear());
        map.put("yearMonth", accountingPeriodItem.getYearMonth());
        map.put("month", accountingPeriodItem.getMonth());
        return map;
    }

    /**
     * 冲红检查
     *
     * @param glVoucher
     */
    private void checkReversal(GLVoucher glVoucher) {
        GLVoucher voucher = glVoucherRepository.findByPeriodAndCompany_IdAndVoucherTemplate_VoucherTemplateGroup_Id(glVoucher.getPeriod(), glVoucher.getCompany().getId(), "998");
        if (voucher == null) throw new RuntimeException(glVoucher.getPeriod() + "未结转，无需冲红，自行修改即可");
    }


    public List<Map<String, String>> findByAuxIdAndAccCode(SimpleRequest request) {
        String id = request.get("id");
        String code = request.get("code");
        return SpringManager.getBean(GLVoucherAuxiliaryRepository.class).findByAuxIdAndAccCode(id, code);
    }

    public Map importData(SimpleRequest request) throws Exception {

        JSONObject jsonData = request.getJO();
        JSONObject importData = jsonData.getJSONObject("importData");
        JSONObject model = jsonData.getJSONObject("model");
        String companyId = model.getJSONObject("company").getString("id");

        Map requestMap = new LinkedHashMap();
        List<Map> items = new ArrayList<>();
        StringBuffer msg = new StringBuffer();
        importData.forEach((k, v) -> {

            JSONArray excl = (JSONArray) v;
            JSONArray header = (JSONArray) excl.get(0); //表格头部

            for (int i = 0; i < excl.size(); i++) {  //遍历每一行
                if (i == 0) continue; //跳过首行

                JSONArray lineData = (JSONArray) excl.get(i); //当前行
                if (lineData.size() == 0) continue;

                Map m = MapUtil.mapper(
                        "accountingOperatingChart", header.indexOf("科目") < lineData.size() ? lineData.getString(header.indexOf("科目")) : null,
                        "summary", header.indexOf("摘要") < lineData.size() ? lineData.getString(header.indexOf("摘要")) : null,
                        "debit", header.indexOf("借") < lineData.size() ? lineData.getString(header.indexOf("借")) : null,
                        "credit", header.indexOf("贷") < lineData.size() ? lineData.getString(header.indexOf("贷")) : null,
                        "aux", header.indexOf("辅助核算") < lineData.size() ? lineData.getString(header.indexOf("辅助核算")) : null,
                        "cashFlow", header.indexOf("现金流量") < lineData.size() ? lineData.getString(header.indexOf("现金流量")) : null,
                        "replaceId", header.indexOf("...") < lineData.size() ? lineData.getIntValue(header.indexOf("...")) : null
                );
                String message = convertData(m, companyId, i);
                msg.append(message);
                items.add(m);
            }
        });
        requestMap.put("msg", msg.toString());
        requestMap.put("items", items);
        return requestMap;
    }

    private String convertData(Map map, String companyId, int index) {
        String message = "";
        Optional<AccountingOperatingChart> accountingOperatingChart = SpringManager.getBean(AccountingOperatingChartRepository.class).findOne(JpaDsl.toCriteriaByEq(
                "name", map.get("accountingOperatingChart").toString().trim(),
                "company.id", companyId,
                "finalFlag", true));
        if (accountingOperatingChart.isPresent()) {
            AccountingOperatingChart operatingChart = accountingOperatingChart.get();
            map.put("accountingOperatingChart", operatingChart);

            /**现金流量**/
            AccountingOperatingChartAuxiliary cashFlowChartAuxiliary =  operatingChart.getAccOperatingChartAuxiliaryList()
                    .stream().filter(item -> item.getAccAuxiliaryProject().isCashFlow()).findFirst().orElse(null);
            if (cashFlowChartAuxiliary != null) {
                String cashFlow = map.get("cashFlow").toString();
                String repository = cashFlowChartAuxiliary.getAccAuxiliaryProject().getRepository();
                Object entity;
                if(Objects.equals("partyRoleRepository",repository)){
                    entity = ((CustomRepository) SpringManager.getBean(repository)).findOne(
                            JpaDsl.toCriteriaByEq("id", cashFlow,
                                    "role.id", StringUtil.toUpperFirst(cashFlowChartAuxiliary.getAccAuxiliaryProject().getBusinessField().getId()))).orElse(null);
                }else{
                    entity = ((CustomRepository) SpringManager.getBean(repository)).findOne(cashFlow);
                }
                if (entity == null) {
                    message = message + "第" + index + "行科目：" + operatingChart.getCode() + " 现金流量: " + cashFlowChartAuxiliary.getAccAuxiliaryProject().getName() + " 值不存在！<br/>";
                } else {
                    map.put("cashFlow", entity);
                }
            }

            /**辅助核算**/
            List<AccountingOperatingChartAuxiliary> chartAuxiliaryList = operatingChart.getAccOperatingChartAuxiliaryList()
                    .stream().filter(item -> !item.getAccAuxiliaryProject().isCashFlow()).collect(Collectors.toList());
            if (!chartAuxiliaryList.isEmpty()) {
                AccountingOperatingChart chart = (AccountingOperatingChart) map.get("accountingOperatingChart");
                if(map.get("aux") == null) throw new RuntimeException("科目：" + chart.getName() + " 需要填写辅助核算");
                List<String> auxDatas = Arrays.asList(map.get("aux").toString().split("[\\,\\;\\，\\s]+"));
                List<GLVoucherAuxiliary> glVoucherAuxiliaries = new ArrayList<>();
                for (int i = 0; i < chartAuxiliaryList.size(); i++) {
                    AccountingOperatingChartAuxiliary chartAuxiliary = chartAuxiliaryList.get(i);
                    String repository = chartAuxiliary.getAccAuxiliaryProject().getRepository();
                    Object entity;
                    if(Objects.equals("partyRoleRepository",repository)){
                        entity = ((CustomRepository) SpringManager.getBean(repository)).findOne(
                                JpaDsl.toCriteriaByEq("id", auxDatas.get(i),
                                        "role.id", StringUtil.toUpperFirst(chartAuxiliary.getAccAuxiliaryProject().getBusinessField().getId()))).orElse(null);
                    }else{
                        entity = ((CustomRepository) SpringManager.getBean(repository)).findOne(auxDatas.get(i));
                    }
                    if (entity == null) {
                        message = message + "第" + index + "行科目：" + operatingChart.getCode() + " 辅助核算项目：" + chartAuxiliary.getAccAuxiliaryProject().getName() + " 值不存在！<br/>";
                        continue;
                    }

                    Map item = MapUtil.mapped(entity);
                    GLVoucherAuxiliary glVoucherAuxiliary = new GLVoucherAuxiliary();
                    glVoucherAuxiliary.setCode(item.get("id").toString());
                    glVoucherAuxiliary.setName(item.get("name").toString());
                    glVoucherAuxiliary.setAccountingOperatingChartAuxiliary(chartAuxiliary);
                    glVoucherAuxiliaries.add(glVoucherAuxiliary);
                }
                if (!glVoucherAuxiliaries.isEmpty()) {
                    map.put("aux", getAux(glVoucherAuxiliaries));
                    map.put("voucherAuxiliaryList", glVoucherAuxiliaries);
                }
            }
        } else {
            message = message + "第" + index + "行科目错误<br/>";
        }
        return message;
    }

    private String getAux(List<GLVoucherAuxiliary> glVoucherAuxiliaries) {
        List combination = new ArrayList();
        glVoucherAuxiliaries.forEach(voucherAuxiliarys -> {
            String auxName = voucherAuxiliarys.getAccountingOperatingChartAuxiliary().getAccAuxiliaryProject().getName();
            String code = voucherAuxiliarys.getCode();
            String name = voucherAuxiliarys.getName();
            if (auxName != null && name != null) {
                combination.add(auxName + ":" + "[(" + code + ")" + name + "]");
            }
        });
        return combination.toString().substring(1, combination.toString().length() - 1);
    }

    /**
     * 财务凭证批量审核功能
     *
     * @param request
     */
    public JSONObject findNoApproveVoucher(SimpleRequest request) {
        JSONObject json = request.getJO();
        String companyId = json.getJSONObject("company").getString("id");
        String period = json.getJSONObject("period").getString("PERIOD_NAME");
        String voucherTemplateId = json.getJSONObject("voucherTemplate").getString("id");
        String personId = ((PartyRole) request.getPerson()).getId();

        OutboxPayloadWrapper wrapper = new OutboxPayloadWrapper();
        wrapper.setSubscriber("local:fin.glVoucher.batch.approve");
        Map payload = MapUtil.mapper("companyId", companyId, "period", period, "voucherTemplateId", voucherTemplateId, "personId", personId);
        wrapper.setPayload(JSON.toJSONString(payload));
        wrapper.setAggregateId(String.format("%s-%s-%s", companyId, voucherTemplateId, period));
        SpringManager.getBean(OutboxEventService.class).sendSaveEvent(wrapper);
        return json;
    }

    @OutboxSubscriber(id = "local:fin.glVoucher.batch.approve", name = "财务凭证批量审核")
    public void approveBatch(OutboxPayloadWrapper outboxPayloadWrapper) {
        JSONObject payload = JSONObject.parseObject(outboxPayloadWrapper.getPayload());
        String companyId = payload.getString("companyId");
        String period = payload.getString("period");
        String voucherTemplateId = payload.getString("voucherTemplateId");
        String personId = payload.getString("personId");
        GLVoucher glVoucher = glVoucherRepository.findFirstByPeriodAndCompany_idAndVoucherTemplate_IdAndStateOrderById(period, companyId, voucherTemplateId, "sent");
        if (glVoucher == null) return;
        String glVoucherId = glVoucher.getId();
        /**批量审核第一行数据漏审**/
        MQUtil.publish("fin.glVoucher.approve", "财务凭证审核", MapUtil.mapper("glVoucherId", glVoucherId, "personId", personId));
        int size = 1000;
        while (true) {
            List<Map> rows = glVoucherRepository.findNoApproveVoucher(period, companyId, voucherTemplateId, "sent", glVoucherId, PageRequest.of(0, size)).getContent();
            rows.stream().forEach(row -> {
                String id = (String) row.get("ID");
                MQUtil.publish("fin.glVoucher.approve", "财务凭证审核", MapUtil.mapper("glVoucherId", id, "personId", personId));
            });
            if (rows.size() < size) {
                break;
            }
            glVoucherId = (String) rows.get(rows.size() - 1).get("ID");
        }
    }

    @Consumer(topic = "fin.glVoucher.approve", backup = false)
    private void pushOutbox(SimpleRequest request) {
        JSONObject payload = request.getJO();
        String glVoucherId = payload.getString("glVoucherId");
        OutboxPayloadWrapper wrapper = new OutboxPayloadWrapper();
        wrapper.setSubscriber("local:fin.glVoucher.batch.handle");
        wrapper.setPayload(payload.toJSONString());
        wrapper.setAggregateId(glVoucherId);
        SpringManager.getBean(OutboxEventService.class).sendSaveEvent(wrapper);
    }

    @OutboxSubscriber(id = "local:fin.glVoucher.batch.handle", name = "财务凭证批量审核执行")
    public void handleBatch(OutboxPayloadWrapper outboxPayloadWrapper) {
        JSONObject payload = JSONObject.parseObject(outboxPayloadWrapper.getPayload());
        String glVoucherId = payload.getString("glVoucherId");
        String personId = payload.getString("personId");
        GLVoucher glVoucher = glVoucherRepository.findOne(glVoucherId);
        if (glVoucher == null) return;
        if (LogicalUtil.in(glVoucher.getState(), "approve", "reversal")) return;
        PartyRole partyRole = SpringManager.getBean(PartyRoleService.class).findOne(personId, "Employee");
        glVoucher.setApproveUID(partyRole);
        approveVoucher(glVoucher, "批量审核凭证");
    }

    public List<GLVoucher> findByIds(SimpleRequest request) {
        List ids = request.getList();
        Sort sort = new Sort(Sort.Direction.ASC, "company.id","yearMonth","voucherNo");
        List<GLVoucher> vouchers = glVoucherRepository.findAll(JpaDsl.toCriteria("id", "IN", ids),sort);
        return vouchers;
    }
}
